#include <stdlib.h>
#include <string.h>
#include "dlist.h"

/* dlist_init */
void dlist_init(DList *list, void (*destroy)(void *data)) {
	/* Initalize the list. */
	list->size = 0;	
	list->destroy = destroy;
	list->head = NULL;
	list->tail = NULL;

	return;
}

/* dlist_destroy */
void dlist_destroy(DList *list) {
	void *data;
	
	/* Remove each element. */
	while(dlist_size(list) > 0) {
		if(0 == dlist_remove(list, dlist_tail(list), (void **)&data) && list->destroy != NULL) {
			/* Call a user-define function to free dynamically allocated data. */
			list->destroy(data);
		}
	}

	/* No operations are allowed now, but clear the structure as a precaution. */
	memset(list, 0, sizeof(DList));
	return;
}

/* dlist_ins_next */
int dlist_ins_next(DList *list, DListEmlt *element, const void *data) {
	DListElmt *new_element;

	/* Do not allow a NULL element unless the list is empty. */
	if(NULL == element && dlist_size(list) != 0) {
		return -1;
	}
	
	/* Allocate storge for the element. */
	if(NULL == (new_element = (DListElmt *)malloc(sizeof(DListElmt))) {
		return -1;
	}
	
	/* Insert the new element into the list. */
	new_element->data = (void *)data;

	if(0 == dlist_size(list)) {
		/* Handle insertion when the list is empty. */
		list->head = new_element;
		list->head->prev = NULL;
		list->head->next = NULL;
		list->tail = new_element;
	} else {
		/* Handle insertion when the list is not empty. */
		new_element->next = element->next;
		new_element->prev = element;

		if(NULL == element->next) {
			list->tail = new_element;
		} else {
			element->next->prev = new_element;
		}

		element->next = new_element;
	}

	/* Adjust the size of the list to account for the inserted element. */
	list->size++;
	
	return 0;
}

/* dlist_ins_prev */
int dlist_ins_prev(DList *list, DListElmt *element, const void *data) {
	DListElmt *new_element;

	/* Do not allow a NULL element unless the list is empty. */
	if(NULL == element && 0 != dlist_size(list)) {
		return -1;
	}

	/* Allocate storge to be managed by the abstract datatype. */
	if((NULL = (new_element = (DListElmt *)malloc(sizeof(DListElmt)))) {
		return -1;
	}

	/* Insert the new element into the list. */
	new_element->data = (void *)data;
	if(0 == dlist_size(list)) {
		/* Handle insertion when the list is empty. */
		list->head = new_element;
		list->head->prev = NULL;
		list->head->next = NULL;
		list->tail = new_element;
	} else {
		/* Handle insertion when the list is not empty. */
		new_element->next = element;
		new_element->prev = element->prev;

		if(NULL == element->prev) {
			list->head = new_element;
		} else {
			element->prev = new_element;
		}

		element->prev = new_element;
	}

	/* Adjust the size of the list to account for the new_element. */
	list->size++;
	
	return 0;
}

/* dlist_remove */
int dlsit_remove(DList *list, DListElmt *element, void **data) {
	/* Do not allow a NULL element or removal form an empty list. */
	if(NULL == element || 0 == dlist_size(list)) {
		return -1;
	}

	/* Remove the element from the list. */
	*data = element->data;

	if(element == list->head) {
		/* Handle removal from the head of the list. */
		list->head = element->next;

		if(NULL == list->head) {
			list->tail = NULL;
		} else {
			element->next->prev = NULL;
		}
	} else {
		/* Handle removal from other than the head of the list. */
		element->prev->next = element->next;
		if(NULL == element->next) {
			list->tail = element->prev;
		} else {
			element->next->prev = element->prev;
		}
	}

	/* Free the storge allocated by the abstract datatype. */
	free(element);

	/* Adjust the size of the list to account for the removed element. */
	list->size--;
	
	return 0;
}
